//
// Created by baigui on 2021/10/29.
//

#include <log.h>
#include <TsDemux.h>
#include "ts/TsFilter.h"


int TsSectionFilter::process(const uint8_t *section, int section_len, int is_start) {

    //目前我们只处理PAT和PMT
    if (mSectionType == PAT) {
//        MpegTSContext *ts = filter->u.section_filter.opaque;
//        MpegTSSectionFilter *tssf = &filter->u.section_filter;
        SectionHeader h1, *h = &h1;
        const uint8_t *p, *p_end;
        int sid, pmt_pid;
//        AVProgram *program;

        LOGV("PAT:\n");

        p_end = section + section_len - 4;
        p = section;
        if (parse_section_header(h, &p, p_end) < 0)
            return 0;
        if (tid != PAT_TID)
            return 0;
//        if (ts->skip_changes)
//            return 0;

        if (skip_identical(h, this))
            return 0;

        for (;;) {
            sid = get16(&p, p_end);
            if (sid < 0)
                break;
            pmt_pid = get16(&p, p_end);
            if (pmt_pid < 0)
                break;
            pmt_pid &= 0x1fff;

            LOGV("sid=0x%x pid=0x%x\n", sid, pmt_pid);

            if (sid == 0x0000) {
                /* NIT info */
            } else {
                mSectionFilterCallBack->OnPatCallBack(pmt_pid, sid);
            }
        }

    } else if (mSectionType == PMT) {

        SectionHeader h1, *h = &h1;
        PESContext *pes;
//        AVStream *st;
        const uint8_t *p, *p_end, *desc_list_end;
        int program_info_length, pcr_pid, pid, stream_type;
        int desc_list_len;
        uint32_t prog_reg_desc = 0; /* registration descriptor */

        int mp4_descr_count = 0;
        int i;

        LOGV("PMT: len %i\n", section_len);

        p_end = section + section_len - 4;
        p = section;
        if (parse_section_header(h, &p, p_end) < 0)
            return 0;
        if (h->tid != PMT_TID)
            return 0;
        if (skip_identical(h, this))
            return 0;

        LOGV("sid=0x%x sec_num=%d/%d version=%d tid=%d\n",
             h->id, h->sec_num, h->last_sec_num, h->version, h->tid);

        pcr_pid = get16(&p, p_end);
        if (pcr_pid < 0)
            return 0;
        pcr_pid &= 0x1fff;
//        add_pid_to_pmt(ts, h->id, pcr_pid);
//        set_pcr_pid(ts->stream, h->id, pcr_pid);

        LOGD("pcr_pid=0x%x\n", pcr_pid);

        // program info descriptor
        program_info_length = get16(&p, p_end);
        if (program_info_length < 0)
            return 0;
        program_info_length &= 0xfff;
        while (program_info_length >= 2) {
            uint8_t tag, len;
            tag = get8(&p, p_end);
            len = get8(&p, p_end);

            LOGD("program tag: 0x%02x len=%d\n", tag, len);

            if (len > program_info_length - 2)
                // something else is broken, exit the program_descriptors_loop
                break;
            program_info_length -= len + 2;
            if (tag == 0x1d) { // IOD descriptor
                get8(&p, p_end); // scope
                get8(&p, p_end); // label
                len -= 2;
//                mp4_read_iods(ts->stream, p, len, mp4_descr + mp4_descr_count,
//                              &mp4_descr_count, MAX_MP4_DESCR_COUNT);
            } else if (tag == 0x05 && len >= 4) { // registration descriptor
//                prog_reg_desc = bytestream_get_le32(&p);
                len -= 4;
            }
            p += len;
        }
        p += program_info_length;
        if (p >= p_end)
            return 1;

//        // stop parsing after pmt, we found header
//        if (!ts->stream->nb_streams)
//            ts->stop_parse = 2;
//
//        set_pmt_found(ts, h->id);


        for (;;) {
            pes = NULL;
            stream_type = get8(&p, p_end);
            if (stream_type < 0)
                break;
            pid = get16(&p, p_end);
            if (pid < 0)
                return 0;
            pid &= 0x1fff;

            MediaStream *stream;

            const StreamType *type = mpegts_find_stream_type(stream_type, ISO_types);

            if (type->codec_type == AVMEDIA_TYPE_VIDEO) {
                stream = new MediaStream;
                stream->type = video;
            } else if (type->codec_type == AVMEDIA_TYPE_AUDIO) {
                stream = new MediaStream;
                stream->type = audio;
            } else {
                continue;
            }

            stream->id = pid;
            stream->codeType = type->codec_id;
            desc_list_len = get16(&p, p_end);
            mSectionFilterCallBack->OnPmtCallBack(pid, stream);
            if (desc_list_len < 0)
                return 0;
            desc_list_len &= 0xfff;
            desc_list_end = p + desc_list_len;
            if (desc_list_end > p_end)
                return 0;
            //这些字段大概是语言等其他信息，暂时不解析。
//            for (;;) {
//                if (ff_parse_mpeg2_descriptor(ts->stream, st, stream_type, &p,
//                                              desc_list_end, mp4_descr,
//                                              mp4_descr_count, pid, ts) < 0)
//                    break;
//
//                if (pes && prog_reg_desc == AV_RL32("HDMV") &&
//                    stream_type == 0x83 &&sub_st) {
//                    av_program_add_stream_index(ts->stream, h->id,
//                                               sub_st->index);
//                   sub_st->codecpar->codec_tag = st->codecpar->codec_tag;
//                }
//            }
            p = desc_list_end;
        }

    }
    return 0;
}

void TsSectionFilter::setSectionFilterCallBack(SectionFilterCallBack *callBack) {
    mSectionFilterCallBack = callBack;

}

TsSectionFilter::TsSectionFilter(SectionFilterType type) {
    mSectionType = type;
}


int TsPESFilter::process(const uint8_t *buf, int buf_size, int is_start) {

//    PESContext *pes   = filter->u.pes_filter.opaque;
//    MpegTSContext *ts =ts;
    const uint8_t *p;
    int ret, len, code;

    if (!pkt)
        return 0;

    if (is_start) {
        if (state == MPEGTS_PAYLOAD && data_index > 0) {
//            ret = new_pes_packet(pkt);
            if (ret < 0)
                return ret;
            stop_parse = 1;
        } else {
            reset_pes_packet_state(this);
        }
        state = MPEGTS_HEADER;
//        ts_packet_pos = pos;
    }
    p = buf;
    while (buf_size > 0) {
        switch (state) {
            case MPEGTS_HEADER:
                len = PES_START_SIZE - data_index;
                if (len > buf_size)
                    len = buf_size;
                memcpy(header + data_index, p, len);
                data_index += len;
                p += len;
                buf_size -= len;
                if (data_index == PES_START_SIZE) {
                    /* we got all the PES or section header. We can now
                     * decide */
                    if (header[0] == 0x00 && header[1] == 0x00 &&
                        header[2] == 0x01) {
                        /* it must be an MPEG-2 PES stream */
                        code = header[3] | 0x100;
                        LOGV("pid=%x pes_code=%#x\n", pid, code);
                        stream_id = header[3];

//                        if ((st &&st->discard == AVDISCARD_ALL &&
//                             (!sub_st ||
//                             sub_st->discard == AVDISCARD_ALL)) ||
//                            code == 0x1be) /* padding_stream */
//                            goto skip;

                        /* stream not present in PMT */
//                        if (!st) {
//                            if (ts->skip_changes)
//                                goto skip;
//
//                           st = avformat_new_stream(ts->stream, NULL);
//                            if (!st)
//                                return AVERROR(ENOMEM);
//                           st->id =pid;
//                            mpegts_set_stream_info(st, pes, 0, 0);
//                        }

                        total_size = AV_RB16(header + 4);
                        /* NOTE: a zero total size means the PES size is
                         * unbounded */
                        if (!total_size)
                            total_size = MAX_PES_PAYLOAD;

                        /* allocate pes buffer */
                        buffer = new block_t(total_size +
                                                 AV_INPUT_BUFFER_PADDING_SIZE);
                        if (!buffer)
                            return AVERROR(ENOMEM);

                        if (code != 0x1bc && code != 0x1bf &&
                            /* program_stream_map, private_stream_2 */
                            code != 0x1f0 && code != 0x1f1 && /* ECM, EMM */
                            code != 0x1ff && code != 0x1f2 &&
                            /* program_stream_directory, DSMCC_stream */
                            code != 0x1f8) {                  /* ITU-T Rec. H.222.1 type E stream */
                            state = MPEGTS_PESHEADER;
//                            if (st->codecpar->codec_id == AV_CODEC_ID_NONE && !st->request_probe) {
//                                LOGV("pid=%x stream_type=%x probing\n",
//                                       pid, stream_type);
//                                st->request_probe = 1;
//                            }
                        } else {
                            pes_header_size = 6;
                            state = MPEGTS_PAYLOAD;
                            data_index = 0;
                        }
                    } else {
                        /* otherwise, it should be a table */
                        /* skip packet */
                        skip:
                        state = MPEGTS_SKIP;
                        continue;
                    }
                }
                break;
                /**********************************************/
                /* PES packing parsing */
            case MPEGTS_PESHEADER:
                len = PES_HEADER_SIZE - data_index;
                if (len < 0)
                    return AVERROR_INVALIDDATA;
                if (len > buf_size)
                    len = buf_size;
                memcpy(header + data_index, p, len);
                data_index += len;
                p += len;
                buf_size -= len;
                if (data_index == PES_HEADER_SIZE) {
                    pes_header_size = header[8] + 9;
                    state = MPEGTS_PESHEADER_FILL;
                }
                break;
            case MPEGTS_PESHEADER_FILL:
                len = pes_header_size - data_index;
                if (len < 0)
                    return AVERROR_INVALIDDATA;
                if (len > buf_size)
                    len = buf_size;
                memcpy(header + data_index, p, len);
                data_index += len;
                p += len;
                buf_size -= len;
                if (data_index == pes_header_size) {
                    const uint8_t *r;
                    unsigned int flags, pes_ext, skip;

                    flags = header[7];
                    r = header + 9;
                    pts = AV_NOPTS_VALUE;
                    pts = AV_NOPTS_VALUE;
                    if ((flags & 0xc0) == 0x80) {
                        pts = pts = ff_parse_pes_pts(r);
                        r += 5;
                    } else if ((flags & 0xc0) == 0xc0) {
                        pts = ff_parse_pes_pts(r);
                        r += 5;
                        dts = ff_parse_pes_pts(r);
                        r += 5;
                    }
                    extended_stream_id = -1;
                    if (flags & 0x01) { /* PES extension */
                        pes_ext = *r++;
                        /* Skip PES private data, program packet sequence counter and P-STD buffer */
                        skip = (pes_ext >> 4) & 0xb;
                        skip += skip & 0x9;
                        r += skip;
                        if ((pes_ext & 0x41) == 0x01 &&
                            (r + 2) <= (header + pes_header_size)) {
                            /* PES extension 2 */
                            if ((r[0] & 0x7f) > 0 && (r[1] & 0x80) == 0)
                                extended_stream_id = r[1];
                        }
                    }

                    /* we got the full header. We parse it and get the payload */
                    state = MPEGTS_PAYLOAD;
                    data_index = 0;
                    if (stream_type == 0x12 && buf_size > 0) {
                        int sl_header_bytes = 0;
//                                read_sl_header(this, &sl, p,buf_size);
                        pes_header_size += sl_header_bytes;
                        p += sl_header_bytes;
                        buf_size -= sl_header_bytes;
                    }
                    if (stream_type == 0x15 && buf_size >= 5) {
                        /* skip metadata access unit header */
                        pes_header_size += 5;
                        p += 5;
                        buf_size -= 5;
                    }
//                    if (ts->fix_teletext_pts
//                        && (st->codecpar->codec_id == AV_CODEC_ID_DVB_TELETEXT
//                            || st->codecpar->codec_id == AV_CODEC_ID_DVB_SUBTITLE)
//                            ) {
//                        AVProgram *p = NULL;
//                        while ((p = av_find_program_from_stream(stream, p, st->index))) {
//                            if (p->pcr_pid != -1 && p->discard != AVDISCARD_ALL) {
//                                MpegTSFilter *f = ts->pids[p->pcr_pid];
//                                if (f) {
//                                    AVStream *st = NULL;
//                                    if (f->type == MPEGTS_PES) {
//                                        PESContext *pcrpes = f->u.pes_filter.opaque;
//                                        if (pcrpes)
//                                            st = pcrst;
//                                    } else if (f->type == MPEGTS_PCR) {
//                                        int i;
//                                        for (i = 0; i < p->nb_stream_indexes; i++) {
//                                            AVStream *pst = stream->streams[p->stream_index[i]];
//                                            if (pst->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
//                                                st = pst;
//                                        }
//                                    }
//                                    if (f->last_pcr != -1 && st && st->discard != AVDISCARD_ALL) {
//                                        // teletext packets do not always have correct timestamps,
//                                        // the standard says they should be handled after 40.6 ms at most,
//                                        // and the pcr error to this packet should be no more than 100 ms.
//                                        // TODO: we should interpolate the PCR, not just use the last one
//                                        int64_t pcr = f->last_pcr / 300;
//                                        st->pts_wrap_reference = st->pts_wrap_reference;
//                                        st->pts_wrap_behavior = st->pts_wrap_behavior;
//                                        if (dts == AV_NOPTS_VALUE || dts < pcr) {
//                                            pts = dts = pcr;
//                                        } else if (st->codecpar->codec_id ==
//                                                   AV_CODEC_ID_DVB_TELETEXT &&
//                                                   dts > pcr + 3654 + 9000) {
//                                            pts = dts = pcr + 3654 + 9000;
//                                        } else if (st->codecpar->codec_id ==
//                                                   AV_CODEC_ID_DVB_SUBTITLE &&
//                                                   dts > pcr + 10 * 90000) { //10sec
//                                            pts = dts = pcr + 3654 + 9000;
//                                        }
//                                        break;
//                                    }
//                                }
//                            }
//                        }
//                    }
                }
                break;
            case MPEGTS_PAYLOAD:
                if (buffer) {
                    if (data_index > 0 &&
                        data_index + buf_size > total_size) {
//todo                        ret = new_pes_packet(this);
                        if (ret < 0)
                            return ret;
                        total_size = MAX_PES_PAYLOAD;
                        buffer = new block_t(total_size +
                                                 AV_INPUT_BUFFER_PADDING_SIZE);
                        if (!buffer)
                            return AVERROR(ENOMEM);
                        stop_parse = 1;
                    } else if (data_index == 0 &&
                               buf_size > total_size) {
                        // pes packet size is < ts size packet and pes data is padded with 0xff
                        // not sure if this is legal in ts but see issue #2392
                        buf_size = total_size;
                    }
                    memcpy(buffer->getbuffer() + data_index, p, buf_size);
                    data_index += buf_size;
                    /* emit complete packets with known packet size
                     * decreases demuxer delay for infrequent packets like subtitles from
                     * a couple of seconds to milliseconds for properly muxed files.
                     * total_size is the number of bytes following pes_packet_length
                     * in the pes header, i.e. not counting the first PES_START_SIZE bytes */
                    if (!stop_parse && total_size < MAX_PES_PAYLOAD &&
                        pes_header_size + data_index == total_size + PES_START_SIZE) {
                        stop_parse = 1;
//  todo                      ret = new_pes_packet(this);
                        if (ret < 0)
                            return ret;
                    }
                }
                buf_size = 0;
                break;
            case MPEGTS_SKIP:
                buf_size = 0;
                break;
        }
    }
    return 0;
}
